# encoding: utf-8
import re, urlparse, logging

def is_abs_url(url):
    return url.startswith(u'http://') or url.startswith(u'https://')

def append_query_string(url, qs):
    if not qs: return url
    if '?' not in url:
        url += '?'
    elif not url.endswith('&'):
        url += '&'
    url += qs
    return url

def is_html(content_type):
    ct = content_type
    if ';' in ct:
        ct = ct[:ct.index(';')]
    return ct.lower() == 'text/html'

def parse_cookie(cookie, url):
    ''' parse set-cookie string to (name, value, domain, path) tuple. 
    if there aren't domain or path, they are extracted from url.
    '''
    parts = cookie.split(';')
    nvpair = parts[0].strip()
    name, value = nvpair.split('=', 1)

    urlparts = urlparse.urlparse(url)
    domain, path = urlparts[1], urlparts[2]
    
    parts = parts[1:]
    for part in parts:
        nvpair = part.strip().split('=', 1)
        if len(nvpair) != 2: continue
        if nvpair[0].lower() == 'domain':
            domain  = nvpair[1]
        elif nvpair[0].lower() == 'path':
            path = nvpair[1]

    return (name, value, domain, path)

def encode_cookie(cookie):
    '''cookie is a (name, value, domain, path) tuple, as returned from parse_cookie.
    it will encode cookie info into a 'name=value' string, where name encode
    (name, domain, path) info.
    '''
    name, value, domain, path = cookie
    return '%s=%s' % (b64encode('c[%s][%s][%s]' % (domain, name, path)), value)

def accept_cookie(cookieinfo, url):
    if not cookieinfo: return False

    urlparts = urlparse.urlparse(url)
    domain, path = urlparts[1], urlparts[2]
    cdomain, cname, cpath = cookieinfo

    if (cdomain.startswith('.') and (domain.endswith(cdomain) or domain == cdomain[1:])) or (cdomain == domain): 
        if (cpath == '' or cpath == '/') or (path.startswith(cpath)):
            return True
    return False



RE_COOKIE = re.compile(r'^c\[([^]]*)\]\[([^]]*)\]\[([^]]*)\]$')
def decode_cookiename(cookiename):
    '''it will extract cookie (domain, name, path) info. It is almost a reverse operation of encode_cookie'''
    try:
        cookiename = b64decode(cookiename)
    except:
        return None
    m = RE_COOKIE.match(cookiename)
    if m:
        domain, name, path = m.group(1), m.group(2), m.group(3)
        return (domain, name, path)
    return None



# base64 character set
_keyStr = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.-_'
CODE_LA = ord('a')
CODE_UA = ord('A')
CODE_LZ = ord('z')
CODE_UZ = ord('Z')
CODE_0 = ord('0')
CODE_9 = ord('9')
_VERSION = '0'

def encode_url(url, seed=None, options=None):
    d = { 'url': url }
    if options:
        d.update(options)
    if seed is None:
        import random
        seed = random.randrange(1000)
        seed = _pad(seed, 3)
    s = u''
    for k, v in d.iteritems():
        s += unichr(len(k)) + k
        s += unichr(len(v)) + v

    return _encode_str(s, seed)

def decode_url(input):
    seed, str = _decode_str(input)
    res = {}
    i = 0
    while i < len(str):
        klen = ord(str[i])
        i += 1
        key = str[i:i+klen]
        i += klen
        vlen = ord(str[i])
        i += 1
        value = str[i:i+vlen]
        i += vlen
        res[key] = value
    return res.pop('url', None), seed, res

def _pad(s, n):
    s = str(s)
    return (n-len(s)) * '0' + s

def _decode_str(s):
    seed = s[1:4]
    text = _rot(s[4:], -int(seed))
    text = b64decode(text)
    return seed, text.decode('utf-8')

def _encode_str(s, seed):
    s = s.encode('utf-8')
    s = b64encode(s)
    return _VERSION + seed + _rot(s, int(seed))

def _rot(s, n):
    res = []
    n_26 = n % 26
    n_10 = n % 10
    for c in s:
        c = ord(c)
        if CODE_LA <= c <= CODE_LZ:
            c += n_26
            if c > CODE_LZ:
                c = CODE_LA + (c - CODE_LZ - 1)
        elif CODE_UA <= c <= CODE_UZ:
            c += n_26
            if c > CODE_UZ:
                c = CODE_UA + (c - CODE_UZ - 1)
        elif CODE_0 <= c <= CODE_9:
            c += n_10
            if c > CODE_9:
                c = CODE_0 + (c - CODE_9 - 1)
        res.append(chr(c))
    return ''.join(res)

def b64encode(s):
    res = []
    leftover = len(s) % 3
    for i in range(0, len(s)-leftover, 3):
        c2 = ord(s[i])
        c1 = ord(s[i+1])
        c0 = ord(s[i+2])
        res.append(_keyStr[(c2 >> 2) & 0x3f])
        res.append(_keyStr[((c2 & 0x3) << 4) | ((c1 >> 4) & 0x0f)])
        res.append(_keyStr[((c1 & 0x0f) << 2) | ((c0 >> 6) & 0x03)])
        res.append(_keyStr[c0 & 0x3f])
    i += 3
    if leftover == 1:
        c2 = ord(s[i])
        res.append(_keyStr[(c2 >> 2) & 0x3f])
        res.append(_keyStr[(c2 & 0x3) << 4])
        res.append(_keyStr[-1])
        res.append(_keyStr[-1])
    elif leftover == 2:
        c2 = ord(s[i])
        c1 = ord(s[i+1])
        res.append(_keyStr[(c2 >> 2) & 0x3f])
        res.append(_keyStr[((c2 & 0x3) << 4) | ((c1 >> 4) & 0x0f)])
        res.append(_keyStr[(c1 & 0x0f) << 2])
        res.append(_keyStr[-1])
    return ''.join(res)

def b64decode(s):
    res = []
    end = len(s)
    if s[-1] == _keyStr[-1]:
        end -= 4
    for i in range(0, end, 4):
        c3, c2, c1, c0 = _keyStr.index(s[i]), _keyStr.index(s[i+1]), _keyStr.index(s[i+2]), _keyStr.index(s[i+3])
        res.append(chr(((c3 << 2)) | ((c2 >> 4) & 0x03)))
        res.append(chr(((c2 & 0x0f) << 4) | ((c1 >> 2) & 0x0f)))
        res.append(chr(((c1 & 0x03) << 6) | (c0 & 0x03f)))
    if end < len(s):
        if s[-2] == _keyStr[-1]:
            c3, c2 = _keyStr.index(s[end]), _keyStr.index(s[end+1])
            res.append(chr(((c3 << 2)) | ((c2 >> 4) & 0x03)))
        else:
            c3, c2, c1 = _keyStr.index(s[end]), _keyStr.index(s[end+1]), _keyStr.index(s[end+2])
            res.append(chr(((c3 << 2)) | ((c2 >> 4) & 0x03)))
            res.append(chr(((c2 & 0x0f) << 4) | ((c1 >> 2) & 0x0f)))
    return ''.join(res)

